package com.ruoyi.common.utils;

import com.alibaba.fastjson2.*;
import com.alibaba.fastjson2.util.JSONObject1O;
import com.google.gson.JsonObject;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;

/**
 * 对象转换器
 * 根据json配置进行，不同对象之间的转换
 *
 * 包含列表的的对象，列表中的转换关系自成体系
 *
 *
 * 对象转为对象
 * 以及纵向对象深度
 *
 * 列表必须转为列表，属性不能映射列表中的子项
 * 列表中的子属性不参与，列表外的压平操作
 */
public class ObjectConverter {
    /**
     * 将source对象根据config配置转换为T对象
     *
     * 对象可以直接进行字段映射；
     * config 数据结构应该为：
     * {
     *     “id”:{"key":"id","type":"v"},
     *     "name":{"key":"shop_name","type":"v"},
     *     "address":{"key":"loc.addr","type":"v"},
     *     "goods_id":{"key":"goods_id_list","type":"list","map":{
     *               "good_id":"product_id"
     *           }}
     * }
     *
     * @param config
     * @param source
     * @param <T>
     * @return
     */
    public static <T> T convert(JSONObject config, Object source,Class<T> clazz) {
            Map<String, Object> obj = flatObject("", source);
            return doConvert(config,obj).toJavaObject(clazz);

    }

    /**
     * 将source对象根据config配置转换为T对象
     * 包含有List或者Array的时候，需要更加细致的处理
     * @param config
     * @param source
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> List<T> convertArray(JSONObject config, Object source,Class<T> clazz) {

            List<Map<String, Object>> maps = flatArray("",source);
            List<T> result=new ArrayList<>();
            for (Map m: maps){
                result.add(doConvert(config,m).toJavaObject(clazz));
            }
            return result;

    }
    public static JSONObject doConvert(JSONObject config,Map<String,Object> source){
        JSONObject result = config.clone();
        for (String key:config.keySet()) {
            JSONObject confVal = config.getJSONObject(key);
            // 如果属性是list类型的，进行list内部的压平转换
            // 这里应该根据配置项判断是否是复杂类型
            if(confVal.getString("type").equalsIgnoreCase("list")){
                // 列表
                // todo 需要考虑单值列表的场景
                JSONObject listConfig = confVal.getJSONObject("map");
                JSONArray valList = JSONArray.of();
                for (Object listItem: (List<Map<String, Object>>)source.get(confVal.getString("key"))) {
                    valList.add(doConvert(listConfig, (Map<String, Object>) listItem));
                }
                result.put(key,valList);
            }else if ("v".equalsIgnoreCase(confVal.getString("type"))){
                result.put(key, source.get(confVal.getString("key")));
            }else{
                // type==object
                // 对象
                result.put(key,doConvert(config, (Map<String, Object>) source.get(confVal.getString("key"))));
            }

        }
        return result;
    }

    private static List<Map<String, Object>> flatArray(String parent,Object source) {
        List<Map<String, Object>> result = new ArrayList<>();

        for (Object o :  ((JSONArray)source)) {
            result.add(flatObject(parent, o));
        }
        return result;
    }

    /**
     * 对象转为 k-v形式
     * 根据层级不同合并不同级别的key
     *
     * @param source
     * @return
     */
    private static Map<String, Object> flatObject(String parent, Object source) {
        Map<String, Object> result = new HashMap<>();
        JSONObject jsonObject = JSONObject.parseObject(JSON.toJSONString(source));
        for (String key : jsonObject.keySet()) {
            Object o = jsonObject.get(key);
            if (isInnerType(o)) {
                result.put(parent + key, o);
            }else if(o instanceof List ) {
                result.put(parent  + key,flatArray("",o));
            } else {
                // 内部数组问题，需要考虑
                result.putAll(flatObject(parent  + key+".", o));
            }
        }
        return result;

    }

    private static boolean isInnerType(Object o) {
        if (o instanceof String ||
            o instanceof Integer ||
            o instanceof Boolean ||
            o instanceof Character ||
            o instanceof Byte ||
            o instanceof Short ||
            o instanceof Long ||
            o instanceof Float ||
            o instanceof Double

        ) {
            return true;
        }
        return false;
    }
}
